#include "stdafx.h"
#include "As.h"


As::As(int id) : Vertex(id)
{
	WasUpdateOnSelectedPaths = true;
	_currentIterationUpdatedPaths.clear();
	_lastIterationUpdatedPaths.clear();
	_selectedPaths.clear();
}


As::~As()
{
	// TODO - delete members(customers, providers and peers, selectedPaths)
}

void As::PrintEdges()
{
	//Set<Edge*>::iterator it;
	//for (it = _edges.begin(); it != _edges.end(); ++it)
	//{
	//	Edge* e = *it; // Note the "*" here
	//	As* dst = static_cast<As*> (e->GetDestination());
	//	std::cout << _name;
	//	if (IsCustomer(dst))
	//	{
	//		std::cout << " <-- ";
	//	} 
	//	else if (IsProvider(dst))
	//	{
	//		std::cout << " --> ";
	//	}
	//	else if (IsPeer(dst))
	//	{
	//		std::cout << " --- ";
	//	}
	//	else if (IsCustomer(dst))
	//	{
	//		std::cout << " Dont Know ";
	//	}
	//	std::cout << dst->GetName() << std::endl;
	//}
	//std::cout << std::endl;
}

void As::AddCustomer(As *as)
{
	AsLink* newEdge = new AsLink(this, as, false);
	_customers.insert(as);
	Vertex::AddEdge(static_cast<Vertex*> (as));
}

void As::AddProvider(As *as)
{
	AsLink* newEdge = new AsLink(this, as, false);
	_providers.insert(as);
	Vertex::AddEdge(static_cast<Vertex*> (as));
}

void As::AddPeer(As *as)
{
	AsLink* newEdge = new AsLink(this, as, true);
	_peers.insert(as);
	Vertex::AddEdge(static_cast<Vertex*> (as));
}

// Check Relation
bool As::IsRelationExist(As* pSender, Set<As*>& pRelation)
{
	return pRelation.find(pSender) != pRelation.end();
}

bool  As::IsPeer(As* pOther)
{
	Set<As*> pRelation = GetPeers();
	return IsRelationExist(pOther, pRelation);
}
bool  As::IsCustomer(As* pOther)
{
	Set<As*> pRelation = GetCustomers();
	return IsRelationExist(pOther, pRelation);
}
bool  As::IsProvider(As* pOther)
{
	Set<As*> pRelation = GetProvides();
	return IsRelationExist(pOther, pRelation);
}


void As::UpdateOnRcvSelectedPathsFromNeighbor(As* pSender, BgpMessage& pMsg)
{

	Map<As*, Vector<As*> >::iterator it;
	for (it = pMsg.UpdatedPaths->begin(); it != pMsg.UpdatedPaths->end(); ++it)
	{
		As* dst = it->first;
		Vector<As*> newPath = it->second;

		//if (newPath.size() == 1 && _name == "src" && sender->GetName() == "c1" && dst->GetName() == "dst")
		//{
		//	std::cout << "DEBUG ";
		//}

		// if !have destination
		// Add Route
		// else
		// if       --------------LP,Local Preference implementation---------------
		//     my = customer, he = customer - choose shortest path
		//     my = customer, he = provider/ my = customer, he = peer - prefer me (dont update)
		//     my = provider, he = customer - prefer him
		//     my = provider, he = provider - choose shortest path
		//     my = provider, he = peer - prefer him
		//     my = peer, he = customer - prefer him
		//     my = peer, he = provider - prefer me (dont update)
		//     my = peer, he = peer - choose shortest path
		
		// Comment - done all conditions to (choose shortest path, prefer him, prefer me) on a bool variables.
		// Then used '?' to do the actions (i.e. dont use if!!!)

		bool isDestinationExist = _selectedPaths.find(dst) != _selectedPaths.end();

		bool isSenderCustomer = IsCustomer(pSender);
		bool isSenderProvider = IsProvider(pSender);
		bool isSenderPeer = IsPeer(pSender);

		bool isCurrentThroughCustomer = false;
		bool isCurrentThroughProvider = false;
		bool isCurrentThroughPeer = false;
		// TODO - consider to change to if(!dstExist) then add the path and exit the function (to reduce the if's)
		if (isDestinationExist)
		{
			As* currentNextHop = _selectedPaths.find(dst)->second.at(0);
			isCurrentThroughCustomer = IsCustomer(currentNextHop);
			isCurrentThroughProvider = IsProvider(currentNextHop);
			isCurrentThroughPeer = IsPeer(currentNextHop);
		}

		// LP in customer -peer-provider context only (without ties and check if the paths size are equal)
		bool shouldPreferMyPath = 
			(isCurrentThroughCustomer && (isSenderPeer || isSenderProvider))||
			(isCurrentThroughPeer && isSenderProvider);

		// LP in customer -peer-provider context only (without ties and check if the paths size are equal)
		bool shouldPreferHim = 
			!shouldPreferMyPath &&
			(
			!isDestinationExist ||
			(isSenderCustomer && ( isCurrentThroughPeer || isCurrentThroughProvider) ) || 
			(isSenderPeer && isCurrentThroughProvider)			
			);

		// LP in customer -peer-provider context only (with ties and check if the paths size are equal)
		shouldPreferMyPath = shouldPreferMyPath ||
			(!shouldPreferHim && _selectedPaths.find(dst)->second.size() <= newPath.size() + 1) ||
			newPath.find(this) != newPath.end(); // avoid routing loops

		// LP in customer -peer-provider context only (with ties and check if the paths size are equal)
		shouldPreferHim = !shouldPreferMyPath;

		Map<As*, Vector<As*> >::iterator selectedPathIt = _selectedPaths.find(dst);

		Vector<As*> newPathWithSender = newPath;
		newPathWithSender.push_front(pSender);
		if (isDestinationExist && !shouldPreferMyPath)
		{
			Pair<As*, Vector<As*> >* newPathWithDest = new Pair<As*, Vector<As*> >(dst, newPathWithSender);
			_currentIterationUpdatedPaths[dst] = newPathWithSender;

			selectedPathIt->second = newPathWithSender;
			WasUpdateOnSelectedPaths = true;
		}
		else if (!isDestinationExist && *this != *dst)
		{
			Pair<As*, Vector<As*> >* newPathWithDest = new Pair<As*, Vector<As*> >(dst, newPathWithSender);
			_currentIterationUpdatedPaths[dst] = newPathWithSender;

			_selectedPaths.insert(*newPathWithDest);
			WasUpdateOnSelectedPaths = true;
		}
	}
}

void As::InitialIntroduceMyselfToNeighbors()
{
	// Fake "update" - update myself that i know myself
	// in order to send it to everyone else
	// this update goes to _lastIterationUpdatedPaths in order not to 
	// corrupt the _currentIterationUpdatedPaths (it is changed during the UpdateOnRcvSelectedPathsFromNeighbor calls)

	Vector<As*>* nullPath = new Vector<As*>();
	Pair<As*, Vector<As*> >* pathToSelf = new Pair<As*, Vector<As*> >(this, *nullPath);
	_lastIterationUpdatedPaths.insert(*pathToSelf);

	BgpMessage initialMsg = BgpMessage(&_lastIterationUpdatedPaths);

	Set<As*>::iterator customersIter;
	for (customersIter = _customers.begin(); customersIter != _customers.end(); ++customersIter)
	{
		(*customersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, initialMsg);
	}

	Set<As*>::iterator peersIter;
	for (peersIter = _peers.begin(); peersIter != _peers.end(); ++peersIter)
	{
		(*peersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, initialMsg);
	}

	Set<As*>::iterator providersIter;
	for (providersIter = _providers.begin(); providersIter != _providers.end(); ++providersIter)
	{
		(*providersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, initialMsg);
	}
}

// The following table indicates whether or not AS
// announces a route to its neighbor depending on its
// relationship to the AS that send the route :
// 			customer peer provider
// customer	    Y	  Y		 Y
// peer			Y     N      N
// provider		Y	  N		 N
// These export rules ensure that no permitted path will have a step or a valley:
void As::BgpIteration()
{
	// Iteration Initialization
	WasUpdateOnSelectedPaths = false;
	_lastIterationUpdatedPaths = _currentIterationUpdatedPaths;
	// _currentIterationUpdatedPaths.clear();

	Map<As*, Vector<As*> >* _currentIterationUpdatedPathsTemp = new Map<As*, Vector<As*> >();
	_currentIterationUpdatedPaths = *_currentIterationUpdatedPathsTemp;

//  To customers - Export all paths
	
	BgpMessage toCustomersMsg = BgpMessage(&_lastIterationUpdatedPaths);
	Set<As*>::iterator customersIter;
	for (customersIter = _customers.begin(); customersIter != _customers.end(); ++customersIter)
	{
		(*customersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, toCustomersMsg);
	}
// -------------

//  To peers and providers - Export via customers paths only

	Map<As*, Vector<As*> > nextHopCustomer_updatedPaths = Map<As*, Vector<As*> >();

	Map<As*, Vector<As*> >::iterator updatedPathsIter;
	for (updatedPathsIter = _lastIterationUpdatedPaths.begin(); updatedPathsIter != _lastIterationUpdatedPaths.end(); ++updatedPathsIter)
	{
		if (IsCustomer(updatedPathsIter->second.at(0)))
		{
			nextHopCustomer_updatedPaths.insert(*updatedPathsIter);
		}
	}
	BgpMessage nextHopCustomersMsg = BgpMessage(&nextHopCustomer_updatedPaths);

	Set<As*>::iterator peersIter;
	for (peersIter = _peers.begin(); peersIter != _peers.end(); ++peersIter)
	{
		(*peersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, nextHopCustomersMsg);
	}

	Set<As*>::iterator providersIter;
	for (providersIter = _providers.begin(); providersIter != _providers.end(); ++providersIter)
	{
		(*providersIter)->UpdateOnRcvSelectedPathsFromNeighbor(this, nextHopCustomersMsg);
	}
// -------------

}

void As::NullifyBgp()
{
	WasUpdateOnSelectedPaths = true;
	_currentIterationUpdatedPaths.clear();
	_lastIterationUpdatedPaths.clear();
	_selectedPaths.clear();
}

